﻿/*              Copyright(c) 2008 Dave Sexton              *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace DaveSexton.DocToMaml
{
	internal abstract class ProjectBase : IProject
	{
		#region Public Properties
		public static readonly Version ProjectFileVersion = new Version(1, 0);

		public string FilePath { get; private set; }
		#endregion

		#region Private / Protected
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ProjectBase" /> class for derived types.
		/// </summary>
		protected ProjectBase(string file)
		{
			if (string.IsNullOrEmpty(file))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "file");

			FilePath = file;
		}
		#endregion

		#region Methods
		public string MakeRelativePath(string path)
		{
			Uri uri = new Uri(path, UriKind.Absolute);
			Uri projectUri = new Uri(FilePath, UriKind.Absolute);		// include file name

			return System.Web.HttpUtility.UrlDecode(projectUri.MakeRelativeUri(uri).ToString());
		}

		public void Load()
		{
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.IgnoreComments = true;
			settings.IgnoreProcessingInstructions = true;
			settings.IgnoreWhitespace = true;

			using (XmlReader reader = XmlReader.Create(FilePath, settings))
			{
				ReadProjectFile(reader);
			}
		}

		public void Save()
		{
			SaveInternal();
		}

		public void SaveAs(string file)
		{
			if (string.IsNullOrEmpty(file))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "file");

			FilePath = file;
			SaveInternal();
		}

		private void SaveInternal()
		{
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.Encoding = Encoding.UTF8;

			using (XmlWriter writer = XmlWriter.Create(FilePath, settings))
			{
				WriteProjectFile(writer);
			}
		}

		protected abstract void ReadProjectFile(XmlReader reader);

		protected static void ReadProjectStart(XmlReader reader)
		{
			reader.ReadToFollowing("project");

			Version version = new Version(reader.GetAttribute("version"));
			Version programVersion = ProjectFileVersion;

			if (version.Major != programVersion.Major && version.Minor != programVersion.Minor)
				throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
					Resources.Errors.InvalidProjectVersion, version, programVersion));
		}

		protected abstract void WriteProjectFile(XmlWriter writer);

		protected virtual void WriteProjectStart(XmlWriter writer)
		{
			writer.WriteStartDocument();
			writer.WriteStartElement("project");
			writer.WriteAttributeString("version", ProjectFileVersion.ToString());
		}

		protected virtual void WriteProjectEnd(XmlWriter writer)
		{
			writer.WriteEndElement();
			writer.WriteEndDocument();
		}
		#endregion

		#region Events
		private readonly object StateChangedEventLock = new object();
		private EventHandler StateChangedEvent;

		/// <summary>
		/// Event raised when project state changes.
		/// </summary>
		public event EventHandler StateChanged
		{
			add
			{
				lock (StateChangedEventLock)
				{
					StateChangedEvent += value;
				}
			}
			remove
			{
				lock (StateChangedEventLock)
				{
					StateChangedEvent -= value;
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="StateChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		protected virtual void OnStateChanged(EventArgs e)
		{
			EventHandler handler = null;

			lock (StateChangedEventLock)
			{
				handler = StateChangedEvent;
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion
	}
}
